/*
 * AbstractAction.java  0.4.0 / Oct 25, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.swing;

import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.Iterator;

import com.cute4j.swing.events.PropertyChangeSupport;
import com.cute4j.swing.properties.AbstractActionProperties;

/**
 * Abstract Action.
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public abstract class AbstractAction implements Action, Cloneable,
                                                AbstractActionProperties {

// Private Fields. -------------------------------------------------------------

  private HashMap properties;

// Protected Fields. -----------------------------------------------------------

  protected boolean enabled;
  protected boolean selected;
  protected PropertyChangeSupport changeSupport;

// Public Constructors. --------------------------------------------------------

  /**
   * Constructs new AbstractAction object.
   */
  public AbstractAction() {
    this(null, (Icon)null);
  }

  /**
   * Constructs new AbstractAction object.
   *
   * @param name the action name
   */
  public AbstractAction(String name) {
    this(name, (Icon)null);
  }

  /**
   * Constructs new AbstractAction object.
   *
   * @param name the action name
   * @param shortDescription the short description
   */
  public AbstractAction(String name, String shortDescription) {
    this(name, (Icon)null);
    if (shortDescription != null) {
      setShortDescription(shortDescription); // NOPMD
    }
  }

  /**
   * Constructs new AbstractAction object.
   *
   * @param name the action name
   * @param icon the icon
   */
  public AbstractAction(String name, Icon icon) {
    enabled = true;
    properties = new HashMap();
    setProperties(name, icon); // NOPMD
  }

  /**
   * Constructs new AbstractAction object.
   *
   * @param name the action name
   * @param icon the icon
   * @param mnemonic the mnemonic
   * @param shortDescription the short description
   */
  public AbstractAction(String name, Icon icon, int mnemonic,
                        String shortDescription) {
    this(name, icon, mnemonic, shortDescription, null);
  }

  /**
   * Constructs new AbstractAction object.
   *
   * @param name the action name
   * @param icon the icon
   * @param mnemonic the mnemonic
   * @param shortDescription the short description
   * @param accelerator the accelerator
   */
  public AbstractAction(String name, Icon icon, int mnemonic,
                        String shortDescription, KeyStroke accelerator) {
    this(name, icon);
    setMnemonicKey(mnemonic); // NOPMD
    if (shortDescription != null) {
      setShortDescription(shortDescription); // NOPMD
    }
    if (accelerator != null) {
      setAcceleratorKey(accelerator); // NOPMD
    }
  }

// Public Static Methods. ------------------------------------------------------

  /**
   * Returns the string value of the key property for the specified action.
   *
   * @param action the action
   * @param key the name of the property
   *
   * @return the string value of the key property
   *
   * @throws NullPointerException if the action is <tt>null</tt>
   */
  public static String getStringValue(Action action, String key) {
    Object value = action.getValue(key);
    if (value instanceof String) {
      return (String)value;
    }
    return null;
  }

  /**
   * Returns the value of the name property for the specified action.
   *
   * @param action the action
   *
   * @return the value of the name property
   *
   * @throws NullPointerException if the action is <tt>null</tt>
   */
  public static String getName(Action action) {
    return getStringValue(action, NAME);
  }

  /**
   * Returns the value of the short description property for the specified
   * action.
   *
   * @param action the action
   *
   * @return the value of the short description property
   *
   * @throws NullPointerException if the action is <tt>null</tt>
   */
  public static String getShortDescription(Action action) {
    return getStringValue(action, SHORT_DESCRIPTION);
  }

  /**
   * Returns the value of the long description property for the specified
   * action.
   *
   * @param action the action
   *
   * @return the value of the long description property
   *
   * @throws NullPointerException if the action is <tt>null</tt>
   */
  public static String getLongDescription(Action action) {
    return getStringValue(action, LONG_DESCRIPTION);
  }

  /**
   * Returns the value of the help description property for the specified
   * action.
   *
   * @param action the action
   *
   * @return the value of the help description property
   *
   * @throws NullPointerException if the action is <tt>null</tt>
   */
  public static String getHelpDescription(Action action) {
    return getStringValue(action, HELP_DESCRIPTION);
  }

  /**
   * Returns the value of the small icon name property for the specified
   * action.
   *
   * @param action the action
   *
   * @return the value of the small icon name property
   *
   * @throws NullPointerException if the action is <tt>null</tt>
   */
  public static String getSmallIconName(Action action) {
    return getStringValue(action, SMALL_ICON_NAME);
  }

  /**
   * Returns the value of the accelerator key name property for the specified
   * action.
   *
   * @param action the action
   *
   * @return the value of the accelerator key name property
   *
   * @throws NullPointerException if the action is <tt>null</tt>
   */
  public static String getAcceleratorKeyName(Action action) {
    return getStringValue(action, ACCELERATOR_KEY_NAME);
  }

// Public Methods. -------------------------------------------------------------

  /**
   * Returns the string value of the property specified by the key.
   *
   * @param key the name of the property
   *
   * @return the string value of the key property
   */
  public String getStringValue(String key) {
    Object value = getValue(key);
    if (value instanceof String) {
      return (String)value;
    }
    return null;
  }

  /**
   * Returns the value of the name property.
   *
   * @return the value of the name property
   */
  public String getName() {
    return getStringValue(NAME);
  }

  /**
   * Sets the value of the name property.
   *
   * @param name the new value of the name property
   */
  public void setName(String name) {
    putValue(NAME, name);
  }

  /**
   * Returns the value of the small icon property.
   *
   * @return the value of the small icon property
   */
  public Icon getSmallIcon() {
    Object icon = getValue(SMALL_ICON);
    if (icon instanceof Icon) {
      return (Icon)icon;
    }
    return null;
  }

  /**
   * Sets the value of the small icon property.
   *
   * @param icon the new value of the small icon property
   */
  public void setSmallIcon(Icon icon) {
    putValue(SMALL_ICON, icon);
  }

  /**
   * Returns the value of the small icon name property.
   *
   * @return the value of the small icon name property
   */
  public String getSmallIconName() {
    return getStringValue(SMALL_ICON_NAME);
  }

  /**
   * Sets the value of the small icon name property.
   *
   * @param iconName the new value of the small icon name property
   */
  public void setSmallIconName(String iconName) {
    putValue(SMALL_ICON_NAME, iconName);
  }

  /**
   * Returns the value of the mnemonic key property.
   *
   * @return the value of the mnemonic key property
   */
  public int getMnemonicKey() {
    Object mnemonic = getValue(MNEMONIC_KEY);
    if (mnemonic instanceof Integer) {
      return ((Integer)mnemonic).intValue();
    }
    return -1;
  }

  /**
   * Sets the value of the mnemonic key property.
   *
   * @param mnemonic the new value of the mnemonic key property
   */
  public void setMnemonicKey(int mnemonic) {
    putValue(MNEMONIC_KEY, new Integer(mnemonic));
  }

  /**
   * Returns the value of the short description property.
   *
   * @return the value of the short description property
   */
  public String getShortDescription() {
    return getStringValue(SHORT_DESCRIPTION);
  }

  /**
   * Sets the value of the short description property.
   *
   * @param shortDescription the new value of the short description property
   */
  public void setShortDescription(String shortDescription) {
    putValue(SHORT_DESCRIPTION, shortDescription);
  }

  /**
   * Returns the value of the long description property.
   *
   * @return the value of the long description property
   */
  public String getLongDescription() {
    return getStringValue(LONG_DESCRIPTION);
  }

  /**
   * Sets the value of the long description property.
   *
   * @param longDescription the new value of the long description property
   */
  public void setLongDescription(String longDescription) {
    putValue(LONG_DESCRIPTION, longDescription);
  }

  /**
   * Returns the value of the help description property.
   *
   * @return the value of the help description property
   */
  public String getHelpDescription() {
    return getStringValue(HELP_DESCRIPTION);
  }

  /**
   * Sets the value of the help description property.
   *
   * @param helpDescription the new value of the help description property
   */
  public void setHelpDescription(String helpDescription) {
    putValue(HELP_DESCRIPTION, helpDescription);
  }

  /**
   * Returns the value of the accelerator key property.
   *
   * @return the value of accelerator key property
   */
  public KeyStroke getAcceleratorKey() {
    Object accelerator = getValue(ACCELERATOR_KEY);
    if (accelerator instanceof KeyStroke) {
      return (KeyStroke)accelerator;
    }
    return null;
  }

  /**
   * Sets the value of the accelerator key property.
   *
   * @param accelerator the new value of the accelerator key property
   */
  public void setAcceleratorKey(KeyStroke accelerator) {
    putValue(ACCELERATOR_KEY, accelerator);
  }

  /**
   * Returns the value of the accelerator key name property.
   *
   * @return the value of accelerator key name property
   */
  public String getAcceleratorKeyName() {
    return getStringValue(ACCELERATOR_KEY_NAME);
  }

  /**
   * Sets the value of the accelerator key property.
   *
   * @param acceleratorName the new value of the accelerator key name property
   */
  public void setAcceleratorKey(String acceleratorName) {
    putValue(ACCELERATOR_KEY_NAME, acceleratorName);
  }

  /**
   * Returns the value of the action command key property.
   *
   * @return the value of action command key property
   */
  public String getActionCommandKey() {
    Object actionCommandKey = getValue(ACTION_COMMAND_KEY);
    if (actionCommandKey instanceof String) {
      return (String)actionCommandKey;
    }
    return null;
  }

  /**
   * Sets the value of the action command key property.
   *
   * @param actionCommandKey the new value of the action command key property
   */
  public void setActionCommandKey(String actionCommandKey) {
    putValue(ACTION_COMMAND_KEY, actionCommandKey);
  }

  /**
   * Sets group of the action's properties.
   *
   * @param name the value of the name property
   * @param icon the value of the small icon property
   * @param mnemonic the value of the mnemonic key property
   * @param shortDescription the value of the short description property
   * @param longDescription the value of the long description property
   * @param accelerator the value of the accelerator key property
   */
  public void setProperties(String name, Icon icon, int mnemonic,
                            String shortDescription, String longDescription,
                            KeyStroke accelerator) {
    if (name != null) {
      setName(name);
    }
    if (icon != null) {
      setSmallIcon(icon);
    }
    setMnemonicKey(mnemonic);
    if (shortDescription != null) {
      setShortDescription(shortDescription);
    }
    if (longDescription != null) {
      setLongDescription(longDescription);
    }
    if (accelerator != null) {
      setAcceleratorKey(accelerator);
    }
  }

  /**
   * Sets group of the action's properties.
   *
   * @param name the value of the name property
   * @param icon the value of the small icon property
   * @param mnemonic the value of the mnemonic key property
   * @param shortDescription the value of the short description property
   * @param accelerator the value of the accelerator key property
   */
  public void setProperties(String name, Icon icon, int mnemonic,
                            String shortDescription, KeyStroke accelerator) {
    setProperties(name, icon, mnemonic, shortDescription, null, accelerator);
  }

  /**
   * Sets group of the action's properties.
   *
   * @param name the value of the name property
   * @param shortDescription the value of the short description property
   */
  public void setProperties(String name, String shortDescription) {
    if (name != null) {
      setName(name);
    }
    if (shortDescription != null) {
      setShortDescription(shortDescription);
    }
  }

  /**
   * Sets group of the action's properties.
   *
   * @param name the value of the name property
   * @param icon the value of the small icon property
   */
  public void setProperties(String name, Icon icon) {
    if (name != null) {
      setName(name);
    }
    if (icon != null) {
      setSmallIcon(icon);
    }
  }

  /**
   * Returns the string array containing property names.
   *
   * @return the string array containing property names
   */
  public Object[] getKeys() {
    return properties.keySet().toArray();
  }

  /**
   * Returns iterator over the keys.
   *
   * @return iterator over the keys
   */
  public Iterator getKeyIterator() {
    return properties.keySet().iterator();
  }

  /**
   * Returns property change listeners.
   *
   * @return property change listeners
   */
  public PropertyChangeListener[] getPropertyChangeListeners() {
    if (changeSupport == null) {
      return PropertyChangeSupport.getEmptyPropertyChangeListenerArray();
    }
    return changeSupport.getPropertyChangeListeners();
  }

// Protected Methods. ----------------------------------------------------------

  /**
   * Fires a bound property change.
   *
   * @param propertyName the property name
   * @param oldValue the old value of the property
   * @param newValue the new value of the property
   */
  protected void firePropertyChange(String propertyName, Object oldValue,
                                    Object newValue) {
    if (changeSupport != null) {
      changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }
  }

  /**
   * Fires a bound property change.
   *
   * @param propertyName the property name
   * @param oldValue the old value of the property (as a boolean)
   * @param newValue the new value of the property (as a boolean)
   */
  protected void firePropertyChange(String propertyName, boolean oldValue,
                                    boolean newValue) {
    if (changeSupport != null) {
      changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }
  }

// Action Interface Implementation. --------------------------------------------

  /**
   * {@inheritDoc}
   */
  public Object getValue(String key) {
    if (key != null) {
      return properties.get(key);
    }
    return null;
  }

  /**
   * {@inheritDoc}
   */
  public void putValue(String key, Object value) {
    if (key != null) {
      if (value == null) {
        if (properties.containsKey(key)) {
          Object oldValue = properties.remove(key);
          firePropertyChange(key, oldValue, null);
        }
      } else {
        Object oldValue = properties.put(key, value);
        firePropertyChange(key, oldValue, value);
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  public boolean isEnabled() {
    return enabled;
  }

  /**
   * {@inheritDoc}
   */
  public void setEnabled(boolean enabled) {
    boolean oldEnabled = this.enabled;
    if (oldEnabled != enabled) {
      this.enabled = enabled;
      firePropertyChange(PROPERTY_ENABLED, oldEnabled, enabled);
    }
  }

  /**
   * {@inheritDoc}
   */
  public boolean isSelected() {
    return selected;
  }

  /**
   * {@inheritDoc}
   */
  public void setSelected(boolean selected) {
    boolean oldSelected = this.selected;
    if (oldSelected != selected) {
      this.selected = selected;
      firePropertyChange(PROPERTY_SELECTED, oldSelected, selected);
    }
  }

  /**
   * {@inheritDoc}
   */
  public void addPropertyChangeListener(PropertyChangeListener listener) {
    if (changeSupport == null) {
      changeSupport = new PropertyChangeSupport(this);
    }
    changeSupport.addPropertyChangeListener(listener);
  }

  /**
   * {@inheritDoc}
   */
  public void removePropertyChangeListener(PropertyChangeListener listener) {
    if (changeSupport != null) {
      changeSupport.removePropertyChangeListener(listener);
    }
  }

// Overridden Methods. ---------------------------------------------------------

  /**
   * {@inheritDoc}
   */
  public Object clone() {
    try {
      AbstractAction clone = (AbstractAction)super.clone();
      clone.properties = (HashMap)properties.clone();
      if (changeSupport != null) {
        clone.changeSupport = (PropertyChangeSupport)changeSupport.clone();
      }
      return clone;
    } catch (CloneNotSupportedException e) {
      throw new Error(e); // NOPMD
    }
  }

}
